// LemCoop
// Copyright (C) 2013  Oliver Naumann <bldrax@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "lobbystate.h"
#include "leveltransitionstate.h"

LobbyState::LobbyState(GameEngine* engine) : GameState(engine)
{
	m_server = nullptr;
	m_client = nullptr;

	m_shutdown_network_on_state_end = true;
	m_game_started = false;

	m_level_chosen = false;
	m_number_of_players_set = 1;
	m_rebuild_color_helpsymbols = false;
	m_color_checkboxes_shown = 0;
}

LobbyState::~LobbyState()
{
	if (m_is_server)
		ServerCleanup();
	if (m_client)
		ClientCleanup();

	al_destroy_font(m_client_list_font);

	delete m_dialog;
}

void LobbyState::Init(bool is_server, std::string client_name)
{
	m_is_server = is_server;
	m_client_name = client_name;

	m_dialog = new Dialog;

	m_client_list_font = al_load_font(STANDARD_FONT.c_str(), STANDARD_FONT_SIZE, NULL);

	if (is_server)
		ServerInit();
	else
		ClientInit();

	// Insert myself into the clients.
	ClientConnection myself;
	myself.set_guid(m_guid);
	myself.set_name(m_client_name);
	for (int p = 0; p < MAX_PLAYERS; p++)
		myself.set_control_at(p, false);
	m_client_list.AddClient(myself);

	// Create the chatbox.
	ALLEGRO_FONT* chatbox_font = al_load_font(STANDARD_FONT.c_str(), STANDARD_FONT_SIZE, NULL);
	TextBox* chatbox = new TextBox("chatbox", 10, 500, 1004, 218, chatbox_font);
	m_dialog->AddElement(chatbox);
}

void LobbyState::SendMyClientInformation()
{
	// Pack my information.
	Job job(ID_CLIENT_INFORMATION);
	RakNet::StringCompressor::Instance()->EncodeString(RakNet::RakString(m_guid.c_str()), 64, job.data);
	RakNet::StringCompressor::Instance()->EncodeString(RakNet::RakString(m_client_name.c_str()), 64, job.data);
	ClientConnection* myself;
	if (m_client_list.GetClient(m_guid, myself))
	{
		for (int p = 0; p < MAX_PLAYERS; p++)
			job.data->Write(myself->control_at(p));
	}

	// If we are the server, we have to set to broadcast flag because we want to send our information to every
	// client. If we are the client, we can leave that out - it will automatically be sent to the server who
	// then spreads it.
	if (m_is_server)
		job.broadcast = true;

	m_work_manager->JobToRemote(job);
}

void LobbyState::ChangeControlInfo()
{
	// Get myself.
	ClientConnection* myself;
	if (m_client_list.GetClient(m_guid, myself))
	{
		// Update my control info from the checkboxes.
		for (int i = 0; i < m_current_level.player_number; i++)
		{
			GuiElement* element;
			if (m_dialog->GetElement("color_checkbox_" + std::to_string(i), element))
			{
				myself->set_control_at(i, ((CheckBox*)element)->checked());
			}
		}
	}

	// Update the helpsymbols.
	m_rebuild_color_helpsymbols = true;

	// Send the change of my client info to everyone.
	SendMyClientInformation();
}

void LobbyState::HandleInput(ALLEGRO_EVENT next_event)
{
	switch (next_event.type)
	{
	case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
		m_dialog->PrimaryMouseButtonDown(next_event.mouse.x, next_event.mouse.y);
		break;

	case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
		m_dialog->PrimaryMouseButtonUp(next_event.mouse.x, next_event.mouse.y);
		break;

	case ALLEGRO_EVENT_MOUSE_AXES:
		m_dialog->MouseAxisChange(next_event.mouse.x, next_event.mouse.y);
		break;

	case ALLEGRO_EVENT_KEY_CHAR:
		m_dialog->CharEntered(next_event.keyboard.keycode, next_event.keyboard.unichar);
		break;
	}
}

void LobbyState::Update(float timing_factor)
{
	if (m_is_server)
		ServerUpdate();
	else
		ClientUpdate();

	for (int i = 0; i < MAX_PLAYERS; i++)
	{
		if (i < m_number_of_players_set)
		{
			if (!m_dialog->ElementExists("color_checkbox_" + std::to_string(i)))
			{
				CheckBox* checkbox = new CheckBox("color_checkbox_" + std::to_string(i),
					10 + 22 * i, 200, 20, 20, std::bind(&LobbyState::ChangeControlInfo, this));
				
				// Also, if the control flag is already true, the box has to be checked initially.
				ClientConnection* myself;
				if (m_client_list.GetClient(m_guid, myself))
					if (myself->control_at(i))
						checkbox->set_checked(true);

				m_dialog->AddElement(checkbox);
			}
		}
		else
		{
			m_dialog->RemoveElement("color_checkbox_" + std::to_string(i));
		}
	}

	// Update the help symbols (but only if the might have changed).
	if (m_rebuild_color_helpsymbols)
	{
		// Go through every position that could possibly exist.
		for (int i = 0; i < MAX_PLAYERS; i++)
		{
			for (int j = 0; j < MAX_PLAYERS; j++)
			{
				// If a client exists on this position, do we have to distinguish a little more. If none exists, we
				// just remove a possibly existing symbol.
				ClientConnection current_client;
				if (m_client_list.GetClient(i, current_client))
				{
					// If a client is on this position, check whether control is true. Is not, remove the possibly
					// existing symbol.
					if (current_client.control_at(j) && m_level_chosen && j < m_current_level.player_number)
					{
						if (!m_dialog->ElementExists("color_control_helpsymbol_" + std::to_string(i) + "_" + std::to_string(j)))
						{
							// If control is true and the symbol does not already exist, create it.
							ALLEGRO_FONT* help_symbol_font = al_load_font(STANDARD_FONT.c_str(), STANDARD_FONT_SIZE, NULL);
							HelpSymbol* help_symbol = new HelpSymbol("color_control_helpsymbol_" + std::to_string(i)
								+ "_" + std::to_string(j), 250 + 30 * j, 10 + 30 * i, 20, 20, help_symbol_font,
								"Player '" + current_client.name() +"' controls faction " + std::to_string(j) + ".");
							m_dialog->AddElement(help_symbol);
						}
					}
					else
					{
						m_dialog->RemoveElement("color_control_helpsymbol_" + std::to_string(i) + "_" + std::to_string(j));
					}
				}
				else
				{
					m_dialog->RemoveElement("color_control_helpsymbol_" + std::to_string(i) + "_" + std::to_string(j));
				}
			}
		}
	}

	m_dialog->Update();
}

void LobbyState::Draw()
{
	for (unsigned i = 0; i < m_client_list.GetSize(); i++)
	{
		ClientConnection current_client;
		m_client_list.GetClient(i, current_client);
		al_draw_textf(m_client_list_font, al_map_rgb(255,255,255), 50, 10 + 30 * i, NULL, "Client %d:  ID: %s    Name: %s",
			i, current_client.guid().c_str(), current_client.name().c_str());

		//DEBUG
		al_draw_textf(m_client_list_font, al_map_rgb(255, 255, 255), 300, 10 + 30 * i, NULL, "%d, %d, %d, %d", current_client.control_at(0),
			current_client.control_at(1),current_client.control_at(2),current_client.control_at(3));
	}

	m_dialog->Draw();

	if (m_is_server)
	{
		al_draw_textf(m_client_list_font, al_map_rgb(255, 255, 255), 740, 10, ALLEGRO_ALIGN_CENTER, "%d", m_number_of_players_set);
	}

	if (m_level_chosen)
	{
		al_draw_textf(m_client_list_font, al_map_rgb(255, 255, 255), 700, 350, NULL,
			"Level: %s (#%d)", m_current_level.name.c_str(), m_current_level.number);
		al_draw_textf(m_client_list_font, al_map_rgb(255, 255, 255), 700, 370, NULL,
			"Number of Players: %d", m_current_level.player_number);
	}
}
